﻿
using System;
using System.Text;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

using alib.Debugging;
using alib.Enumerable;


#if UNIF_V6
namespace agree
{
	public unsafe sealed partial class Unification : IDisposable
	{
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		uint ugen;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public uint cgen;

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if UNIF_V7
		ArrayTfs _write_tfs_v6(Scratch* ps, int ix_outer = 1)
#else
		ArrayTfs writeU(Slot* ps /*, int ix_outer = 1*/)
#endif
		{
			Debug.Assert(ps->forward == 0 && ps->m_src == Tfs.TopmostMark);

			int uix_upper = (int)((long)ps - (long)ps_base);
			Edge top_edge = new Edge(ps->f, Tfs.TopmostMark);

			ugen = (uint)(gen + 6);
			cgen = (uint)(gen + 7);
			next_mark = 2;
			next_coref_mark = -1;

#if false
			Debug.Assert(count_p1 == zz19_dbg_counted_via_parent.Length);
			Debug.Assert(count_p1 == _count_cnt(0));

			int rec3 = ActiveSlots.Count - c_init_nodes + count_p1;
			int cp1_0 = count_p1;
			int init_nodes0 = c_init_nodes;

			int c_extra = 0;
			int c_extra_top = 0;
			int init_nodes1 = 0;
			int init_nodes2 = 0;

			StringBuilder s_info = null;

			saved_state ss = default(saved_state);
			saved_state ss1 = default(saved_state);

			UnifierSlot[] rg_skipped = null;

			//if ((opts & (Tfs.Flags.Restrict | Tfs.Flags.DeleteArgs)) == Tfs.Flags.Restrict)
			//if ((opts & (Tfs.Flags.Restrict | Tfs.Flags.DeleteArgs)) == Tfs.Flags.DeleteArgs)
			if (hs_skip != null)
			{
				Debug.Assert(_count_gen(0, 1) == 0);
				Debug.Assert(_count_gen(0, 2) == 0);
				//Debug.Assert((ps_base + ((pbx + ps->tfsix)->uix + ix_outer))->gen >= gen);
				Debug.Assert(ps->gen == gen && ps->c_cnt == 0 && ps->forward == 0);

				rg_skipped =
					//alib.Unsafe.Ext.ToArray<IntPtr>(pps_skipped, (int)(pps_skip - pps_skipped))
					// .Distinct()
						hs_skip
						 .Select(x => new UnifierSlot(x))
						 .ToArray();

				ss1 = new saved_state(this);

				if (pbx[1].tfs.id == 0x29b96 && pbx[2].tfs.id == 0x2a220 && rg_skipped.Length == 12)
					Nop.X();

				int ixo = (opts & Tfs.Flags.DeleteArgs) != 0 ? ix_outer : -1;

				c_extra_top = _tfs_descent_extra(ixo, pbx + ps->tfsix, *(ushort**)((long)rgpfix + ((int)ps->f << 3)), ps->m_src);
				init_nodes2 = c_init_nodes;

				s_info = new StringBuilder();
				s_info.AppendFormat("{0,5}  tfs:{1,-2}  m_src:{2,-3}  gen:{3}  c:{4,-3}  {5}\r\n",
						"top",
						ps->tfsix,
						ps->m_src,
						ps->Gen,
						c_extra_top,
						tm.GetEdgeType(ps->f).Name);

				if ((opts & Tfs.Flags.DeleteArgs) != 0)
					Nop.X();

				for (int i = 0; i < rg_skipped.Length; i++)
				{
					Scratch* sk = rg_skipped[i].ps;

					Debug.Assert(sk->gen >= gen);
					Debug.Assert((sk->f & Edge.Flag.EtmNonBareType) != 0);

					int g = sk->Gen;

					Debug.Assert(g != -1 && g != 1 && g != 2);
#if true
					if (g == 4)
					{
						Nop.X();
					}
					else if (g == 3)
					{
						Nop.X();
					}
					else if (sk->forward < 0)
					{
						Nop.X();
					}
					else if (sk->c_cnt != 0)
					{
						Nop.X();
					}
					else
#else
					if (g == 5)
#endif
					{
						int cc = _tfs_descent_extra(
									-1,
									pbx + sk->tfsix,
									*(ushort**)((long)rgpfix + ((int)sk->f << 3)),
									sk->m_src);

						s_info.AppendFormat("{0,5}  tfs:{1,-2}  m_src:{2,-3}  gen:{3}  c:{4,-3}  {5}\r\n",
												i,
												sk->tfsix,
												sk->m_src,
												g,
												cc,
												tm.GetEdgeType(sk->f).Name);
						//Debug.Assert(sk->c_cnt == 0);
						c_extra += cc;

						sk->gen = gen + 4;
					}
				}

				init_nodes1 = c_init_nodes;

				//rec3 = ActiveSlots.Count - c_init_nodes + (count_p1 + c_extra + c_extra_top) - _count_old(0);
				rec3 = count_p1 + c_extra + c_extra_top;

				ss = new saved_state(this);

				Nop.X();
			}
#endif

			count_p3 = 0;
#if CP2
			_count_prep(
				*(ushort**)((long)rgpfix + ((int)ps->f << 3)),
				pbx + ps->tfsix,
				uix_upper,
				ps->m_src);
#else
			_count_prep(ps, ix_upper, ps->m_src);
#endif

			if ((opts & Tfs.Flags.RootCoverage) != 0 && (top_edge.FlagsId & Edge.Flag.EtmNonBareType) != 0)
				_add_uncovered_root_feats(
								*(ushort**)((long)rgpfix + ((int)top_edge.FlagsId << 3)),
								pbx + ps->tfsix,
								uix_upper);

			ArrayTfs tfs = new ArrayTfs(r.FeatMgr, this, opts, top_edge, -next_coref_mark - 1);

#if false
			//if ((opts & (Tfs.Flags.Restrict | Tfs.Flags.DeleteArgs)) == Tfs.Flags.Restrict)
			{
				_dbg_count_display(ps, rec3, count_p3);
			}

			//if ((opts & (Tfs.Flags.Restrict | Tfs.Flags.DeleteArgs)) == Tfs.Flags.Restrict)
			//if ((opts & (Tfs.Flags.Restrict | Tfs.Flags.DeleteArgs)) == Tfs.Flags.DeleteArgs)
			if (hs_skip != null)
			{
				if (rec3 != count_p3)
				{
					Debug.Assert((opts & Tfs.Flags.DeleteArgs) != 0);

					if (rec3 - count_p3 < -2)
						Nop.X();

					ss1.Restore();

					if (rec3 - count_p3 < -2)
						Nop.X();

					ss.Restore();

					//Console.WriteLine("\r\n" + s_info.ToString());

					if (rec3 - count_p3 < -2)
						Nop.X();

					Nop.X();

					//if (zz00_dbg_participants.Any(up => up.tfs.AnyRestriction))
					//    Nop.X();
				}
				else
				{
					ss.Restore();

					Nop.X();
				}
			}
#endif
#if !UNIF_V7
			freeU();
#endif
			return tfs;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		int _tfs_descent_extra(int ix_outer, pinfo* pi, ushort* pfix, int m)
		{
			Debug.Assert(m != 0);
			Slot* nps;
			int c = pfix[-1], ix;
			do
			{
				if ((ix = pi->_fetch_ix_only(*pfix, m)) == 0)
				{
					c--;
				}
				else if (pi->tfsix == 1 && ix == ix_outer)
				{
					throw new Exception();
				}
				else
				{
					Slot* npsc;
					nps = (Slot*)((long)ps_base + ((pi->uix0 + ix) << SHL_PS));

#if PRE_PLUG
					if (nps->gen == gen + 5)
					{
						npsc = nps;
#if P1_COUNT
						Debug.Assert(npsc->c_cnt == 0 && npsc->gen == gen + 5 && npsc->forward == 0);
#endif
						goto npsc_descend;
					}
					else
#endif
					if (nps->gen < gen)
					{
						Edge ne = pi->entries[ix - 1].e;

						nps->tfsix = (short)pi->tfsix;
#if P1_COUNT
						nps->c_cnt = 0;
#endif
						nps->gen = gen + 4;
						c_init_nodes++;
#if DEBUG
						nps->forward = 0;
						nps->e_ul = *(ulong*)&ne & 0xFFFFFFFF7FFFFFFF;
#endif

						if (ne.Mark < 0)
						{
							Slot** pps;
							if ((npsc = *(pps = pi->coref_designations - ne.Mark)) != null)
							{
								Debug.Assert(nps != npsc && npsc->gen >= gen);
								Debug.Assert(npsc->gen == gen || npsc->gen == gen + 3 || npsc->gen == gen + 4 || npsc->gen == gen + 5);
								Debug.Assert(npsc->gen != gen + 5 || npsc->forward == 0);

								Slot* npsd = npsc;

								if (npsc->gen == gen + 3)
								{
									// this one shouldn't happen
									Nop.X();
								}

								if (npsc->forward < 0)
								{
									int fwd;
									while ((fwd = npsc->forward) < 0)
										npsc = (Slot*)((long)ps_base - fwd);

									Debug.Assert(npsc->gen == gen || npsc->gen == gen + 3 || npsc->gen == gen + 4 || npsc->gen == gen + 5);
								}

#if PRE_PLUG
								if (npsc->gen == gen + 5)
								{
									goto g5_descend;
									continue;
								}
#endif

								if ((npsc->f & Edge.Flag.EtmNonBareType) != 0)
								{
#if P1_COUNT
									if (npsc->gen != gen + 4 && npsc->c_cnt == 0)
									{
										goto g5_descend;
									}
#endif
								}

								nps->forward = (int)((long)ps_base - (long)npsc);

								continue;
							}
							*pps = nps;
						}
						nps->forward = 0;
						nps->e_ul = *(ulong*)&ne & 0xFFFFFFFF7FFFFFFF;

						if ((nps->f & Edge.Flag.EtmNonBareType) != 0)
						{
							Debug.Assert(nps->tfsix == pi->tfsix);
							c += _tfs_descent_extra(ix_outer, pi, *(ushort**)((long)rgpfix + ((int)nps->f << 3)), nps->m_src);
						}
					}
					continue;

#if PRE_PLUG
				g5_descend:
					Debug.Assert(npsc->forward == 0);
					nps->forward = (int)((long)ps_base - (long)npsc);

					if (npsc->gen == gen + 3 && nps->gen != gen + 3)
					{
						Nop.X();
					}

				npsc_descend:

					npsc->gen = gen + 4;
					if ((npsc->f & Edge.Flag.EtmNonBareType) != 0)
					{
						c += _tfs_descent_extra(ix_outer, pbx + npsc->tfsix, *(ushort**)((long)rgpfix + ((int)npsc->f << 3)), npsc->m_src);
					}
#endif
				}
			}
			while (*++pfix != 0xFFFF);
			return c;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void _add_uncovered_root_feats(ushort* pfix, pinfo* pi, int ix_upper)
		{
			Slot* nps;
			int ix;
			Edge e;
			Slot* nxt = ps_last;
			do
			{
				if ((ix = pi->_fetch_ix(*pfix, Tfs.TopmostMark, &e)) == 0)
					throw new Exception();

				if ((nps = ps_base + pi->uix0 + ix) == null)
					nps = nxt++;
				else if (nps->gen == cgen)
					continue;

				nps->e = default(Edge);
				nps->forward = 0;
				nps->i_feat = *pfix;
				nps->ix_upper = ix_upper;
				nps->d_over = 0;
				nps->gen = cgen;

				count_p3++;
			}
			while (*++pfix != 0xFFFF);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// now that we are not doing fallback, all accesses within a call to this function *start* within
		/// the same source TFS (but may be forwarded to others)
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if CP2
		void _count_prep(ushort* pfix, pinfo* pi, int ix_upper, int m_upper)
		{
			Slot* nps, ps_act;
			int d, fwd;
			Edge e;
			int ix, _uix;

			do
				if ((ix = pi->_fetch_ix_only(*pfix, m_upper)) != 0)
				{
					d = 0;
					_uix = (ix + pi->uix0) << SHL_PS;
					if ((ps_act = (Slot*)((long)ps_base + _uix))->gen < gen)
					{
						if ((e = ((arr_tfs_entry*)((long)pi->entries + ((ix - 1) << 4)))->e).FlagsId < 0)
						{
							Slot** pps;
							if (*(pps = pi->coref_designations - e.Mark) != null)
							{
								nps = *pps;
#if DEBUG
								ps_act->forward = -nps->uix << SHL_PS;
#endif
								if ((fwd = nps->forward) == 0)
									d = (int)((long)ps_base - (long)nps);
								goto fwd;
							}
							*pps = ps_act;
						}
						ps_act->forward = 0;
						ps_act->tfsix = (short)pi->tfsix;
						ps_act->e_ul = *(ulong*)&e & 0xFFFFFFFF7FFFFFFF;
						nps = ps_act;
						goto no_fwd;
					}
					nps = ps_act;
					fwd = nps->forward;
				fwd:
					while (fwd < 0)
						fwd = (nps = (Slot*)((long)ps_base - (d = fwd)))->forward;

				no_fwd:
					if (nps->gen < ugen)
					{
						nps->gen = ugen;
						if ((nps->f & Edge.Flag.EtmNonBareType) == 0)
						{
							nps->m_src = 0;
						}
						else
						{
							_count_prep(
								*(ushort**)((long)rgpfix + ((int)nps->f << 3)),
								pbx + nps->tfsix,
								d != 0 ? -d : _uix,
								nps->m_src);
							nps->m_src = (int)next_mark;
							next_mark++;
						}
					}
					else if (nps->m_src >= 0)
					{
						nps->m_src = next_coref_mark;			// 2nd visit, switch to coreferenced
						next_coref_mark--;
						nps->f |= Edge.Flag.Coreference;
					}

					/////////////////////
					ps_act->i_feat = *pfix;
					ps_act->ix_upper = ix_upper;
					ps_act->d_over = (short)(d == 0 ? 0 : (d >> SHL_PS));
					//ps_act->d_over = d;
					ps_act->gen = cgen;
#if DEBUG
					/// setting an active generation as we just did means the debugging code will expect that
					/// the slot is initialized, which may not be true for first visits to outer substructure
					//ps_act->tfsix = (short)tfsix;
#endif
					count_p3++;
				}
			while (*++pfix != 0xFFFF);
		}
#else
		void _count_prep(Scratch* ps_upper, int ix_upper, int m_upper)
		{
			Debug.Assert((ps_upper->f & Edge.Flag.EtmNonBareType) != 0);
			Debug.Assert(m_upper != 0 && ps_upper->forward == 0);

			Scratch* nps, ps_act;
			int d = 0, fwd;
			Edge e;
			pinfo* pi = pbx + ps_upper->tfsix;
			int ix;

			/// shifting off the non-bare and coreference bits while also multiplying for pointer arithmetic
			ushort* pfix = *(ushort**)((long)rgpfix + ((int)ps_upper->f << 3));
			do
				if (*pfix != ifeat_restrict && (ix = pi->_fetch_ix(*pfix, m_upper, &e)) != 0)
				{
					ps_act = (Scratch*)((long)ps_base + (ix = (ix + pi->uix) << SHL_PS));
					if ((nps = ps_act)->gen >= gen)
						while ((fwd = nps->forward) < 0)
							nps = (Scratch*)((long)ps_base - (d = fwd));
					else
					{
						if (e.FlagsId < 0)
						{
							Scratch** pps;
							if (*(pps = pi->coref_designations - e.Mark) != null)
							{
								if ((fwd = (nps = *pps)->forward) == 0)
									d = (int)((long)ps_base - (long)nps);
								else
									do
										nps = (Scratch*)((long)ps_base - (d = fwd));
									while ((fwd = nps->forward) < 0);
								ps_act->forward = d;
								goto no_init;
							}
							*pps = nps;
						}
						nps->_init_node(pi->tfsix, e, gen);
					}

				no_init:
					if (nps->gen < ugen)
					{
						nps->gen = ugen;
						if ((nps->f & Edge.Flag.EtmNonBareType) == 0)
						{
							nps->m_src = 0;
						}
						else
						{
							_count_prep(nps, d != 0 ? -d : ix, nps->m_src);
							nps->m_src = (int)next_mark;
							next_mark++;
						}
					}
					else if (nps->m_src >= 0)
					{
						nps->m_src = next_coref_mark;			// 2nd visit, switch to coreferenced
						next_coref_mark--;
						nps->f |= Edge.Flag.Coreference;
					}

					/////////////////////
					ps_act->i_feat = *pfix;
					ps_act->ix_upper = ix_upper;
					if (d == 0)
						ps_act->d_over = 0;
					else
					{
						ps_act->d_over = (short)(d >> SHL_PS);
						d = 0;
					}
					ps_act->gen = cgen;
#if DEBUG
					/// setting an active generation as we just did means the debugging code will expect that
					/// the slot is initialized, which may not be true for first visits to outer substructure
					//ps_act->tfsix = (short)tfsix;
#endif
					count_p3++;
				}
			while (*++pfix != 0xFFFF);
		}
#endif
	};
}
#endif // UNIF_V6
